#ifndef FILE_H
#define FILE_H

#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h> 
#include <stdio.h> 
#include <errno.h>
#include <string>

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <muduo/base/Types.h>
#include <muduo/base/Logging.h>

#include <sdfs/common/ToolKit.h>
#include <sdfs/common/PoolInterface.h>
#include <sdfs/common/PoolData.h>


namespace fs = boost::filesystem;
using namespace muduo;

const char HEAD[]="SDFS BLOCK FILE\n";
const char TAIL[]="SDFS BLOCK END\n";

namespace sdfs
{

namespace common
{

class Directory
{
private:
	Directory()
	{}
public:
	~Directory(){}

	static bool createDirectories(const muduo::string& fullpath)
	{
		fs::path p(fullpath.c_str());
		if(!fs::exists(p))
			return fs::create_directories(p);
		return true;
	}



	static shared_ptr<std::vector<muduo::string> > listDirectory(const muduo::string& fullpath, muduo::string& filter)
	{
		shared_ptr<std::vector<muduo::string> > files( new std::vector<muduo::string>());
		fs::path p(fullpath.c_str());
		assert(fs::exists(p) == true);
		fs::directory_iterator it(p);
		fs::directory_iterator itend;
		for (; it != itend; it++)
		{
			if(fs::is_regular_file(*it))
			{
				fs::path cp = *it;
				if(sdfs::ToolKit::equels(cp.extension().c_str(), filter.c_str()))
				{
					files->push_back(cp.c_str());
				}
			}
		}
		return files;
	}


	shared_ptr<std::vector<muduo::string> > listDirectoryAll(const muduo::string& fullpath)
	{
		shared_ptr<std::vector<muduo::string> > files( new std::vector<muduo::string>());
		fs::path p(fullpath.c_str());
		assert(fs::exists(p) == true);
		fs::directory_iterator it(p);
		fs::directory_iterator itend;
		for (; it != itend; it++)
		{
			if(fs::is_regular_file(*it))
			{
				fs::path cp = *it;
				files->push_back(cp.c_str());
			}
		}
		return files;
	}
	/* data */
};

class File
{
public:
	File(muduo::string& fullname, int flag, bool create, long maxsize):lastPos_(0)
	{
		// LOG_INFO << "file: " << fullname;
		fd_ = ::open(fullname.c_str(), flag);
		if(fd_ == -1 && create)
		{
			LOG_DEBUG << "create file: " << fullname << "size: "<< maxsize;
			char buf[1024];
			
			::memset(buf, 0, 1024);
			fd_ = ::open(fullname.c_str(), flag| O_CREAT| O_TRUNC ,
				 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IROTH);
			assert(fd_ != -1);
		// if (::lseek(fd_, maxsize, SEEK_SET) < 0) 
		// {
//             	LOG_ERROR << "lseek error";
//             	exit(1);
//       		}
			int nwrite = 0;
			int n = 0;
			long curpost = 0;
			if (::write(fd_, HEAD, sizeof(HEAD) ) < 0) 
			{
				LOG_ERROR << "write error";
	               exit(1);
			}
			if (::write(fd_, &curpost, sizeof(curpost) ) < 0) 
			{
				LOG_ERROR << "write error";
	               exit(1);
			}
			error = fallocate(fd_, 0, sizeof(HEAD)+sizeof(long) , maxsize);
		  
		    if (error < 0) {  
		        LOG_ERROR<<"fallocate failed";  
		        exit(EXIT_FAILURE);  
		    }  
			// while(nwrite < maxsize)
			// {
			// 	n = ::write(fd_, buf, sizeof(buf) );
			// 	if (n < 0) 
	  //        		{
	  //        			LOG_ERROR << "write error";
	  //              	exit(1);
	  //        		}
			// 	nwrite += n;
			// }
         		if (::write(fd_, TAIL, sizeof(TAIL) ) < 0) 
			{
				LOG_ERROR << "write error";
	               exit(1);
			}
			
		}
		// LOG_DEBUG << "errno: " << ::strerror(errno);
		seekFromBeginning(0- sizeof(long));
		read(&lastPos_, sizeof(lastPos_));
		assert(fd_ != -1);
	}
	~File()
	{
		// LOG_INFO << "close<fd>";
		int r = ::close(fd_);
		if(r != 0)
			LOG_ERROR << "close error: " << r;
	}

	void seekFromBeginning(long offset)
	{
		::lseek(fd_, offset+sizeof(HEAD) + sizeof(long), SEEK_SET);
		// LOG_DEBUG << "sizeof HEAD: " << sizeof(HEAD) << ", sizeof long: " << sizeof(long);
		// LOG_DEBUG << "lseek pos: " << pos;
	}

	int read(void* buf, long size)
	{
		return ::read(fd_, buf, size);
	}

	int write(const void* buf, long size)
	{
		// long pos = ::lseek(fd_, 0, SEEK_CUR);
		// LOG_DEBUG << "write pos: "<< pos;
		return ::write(fd_, buf, size);
	}

	void updateLastPos(long pos)
	{
		assert(pos > lastPos_);
		::lseek(fd_, sizeof(HEAD), SEEK_SET);
		::write(fd_, &pos, sizeof(pos));
		lastPos_ = pos;
	}

	long lastPos()
	{
		return lastPos_;
	}

private:
	int fd_;
	long lastPos_;
};

class ReadOnlyFile
{
public:
	ReadOnlyFile(muduo::string& fullname): 
		file_( new File(fullname, O_RDONLY, false, 0)),
		pool_(NULL)
	{
	}
	~ReadOnlyFile()
	{
		// LOG_INFO << "~ReadOnlyFile";
	}

	int read(void* data, long length)
	{
		return file_->read(data, length);
	}

	void seekFromBeginning(long offset)
	{
		file_->seekFromBeginning(offset);
	}

	long lastPos()
	{
		long pos;
		file_->seekFromBeginning(0- sizeof(long));
		file_->read(&pos, sizeof(pos));
		return pos;
	}

	void registerToPool(Pool<ReadOnlyFile>* pool)
	{
		pool_ = pool;
		pool_->addToPool(this);
	}

	void unregisterPool()
	{
		pool_ = NULL;
	}

	Pool<ReadOnlyFile>* getPool()
	{
		return pool_;
	}

	/* data */
private:
	shared_ptr<File> file_;
	Pool<ReadOnlyFile>* pool_;
};


class WriteOnlyFile
{
public:
	WriteOnlyFile(muduo::string& fullname, long maxsize): 
		file_(new File(fullname, O_WRONLY, true, maxsize))
	{
	}
	~WriteOnlyFile()
	{
	}

	void seekFromBeginning(long offset)
	{
		file_->seekFromBeginning(offset);
	}

	int write(const void* data, long length)
	{
		return file_->write(data, length);
	}

	void updateLastPos(long pos)
	{
		file_->updateLastPos(pos);
	}

	long lastPos()
	{
		return file_->lastPos();
	}

	void registerToPool(Pool<WriteOnlyFile>* pool)
	{
		pool_ = pool;
		pool_->addToPool(this);
	}

	void unregisterPool()
	{
		pool_ = NULL;
	}

	Pool<WriteOnlyFile>* getPool()
	{
		return pool_;
	}

private:
	shared_ptr<File> file_;
	Pool<WriteOnlyFile>* pool_;
};

typedef shared_ptr<ReadOnlyFile> ReadOnlyFilePtr;
typedef shared_ptr<WriteOnlyFile> WriteOnlyFilePtr;

}

}

#endif
